<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import { Base } from '../threejs/base';
import * as THREE from 'three';

class Demo extends Base {
  mesh;
  m;
  time;
  controls = { color: 0xff0000 };
  geometry = { Geometry: 1 };
  initGui() {
    const g = this.gui.addFolder('调整这里试试');
    let colorChange = g.addColor(this.controls, 'color').name('色值');
    colorChange.onChange((value) => {
      console.log(value);
      for (let i = 0; i < this.scene.children.length; i++) {
        // 方式1
        // this.scene.children[i].material.uniforms.uColor.value.set(value); // 改变颜色为绿色
        // 方式2
        var newColor = new THREE.Color(this.controls.color);
        // 更新材质的颜色
        console.log(newColor);
        this.scene.children[i].material.uniforms.uColor.value.copy(newColor); // 如果颜色是uniform
      }
    });
    g.open();
  }

  initShader() {
    this.time = 0;
    this.ini01(
      0,
      0,
      `
      uniform float uTime;
      varying vec2 vUv;
      void main() {
        vUv = uv;
        vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
        // gl_PointSize = 7.0;
        // gl_PointSize = 100.0 / -mvPosition.z;
        gl_PointSize = 100.0 / -mvPosition.z;
        gl_Position = projectionMatrix * mvPosition;
      }
    `,
      `
      varying vec2 vUv;
      void main() {
        // vUv.x => 0.0-1.0
        gl_FragColor = vec4(vUv.x, 0.0, 0.0, 1.0);
      }
    `
    );
  }
  ini01(x, y, vShader = '', fShader = '', wireframe = true) {
    this.renderer.setClearColor(0x160016, 1);
    const count1 = 50000;
    const count2 = 100000;

    const geometry = new THREE.BufferGeometry();
    const positions = [];
    const sizes = [];
    const shifts = [];
    for (let i = 0; i < count1 + count2; i++) {
      let theta = Math.random() * Math.PI * 2;
      // let phi = Math.random() * Math.PI;
      let phi = Math.acos(Math.random() * 2 - 1);
      let angle = (Math.random() * 0.9 + 0.1) * Math.PI * 0.1;
      let strength = Math.random() * 0.9 + 0.1; // 0.1-1.0
      shifts.push(theta, phi, angle, strength);

      let size = Math.random() * 1.5 + 0.5; // 0.5-2.0
      sizes.push(size);

      if (i < count1) {
        // 中心球体粒子
        // let r = 10;
        let r = Math.random() * 0.5 + 9.5;
        // let x = r * Math.sin(phi) * Math.cos(theta);
        // let y = r * Math.sin(phi) * Math.sin(theta);
        // let z = r * Math.cos(phi);
        let { x, y, z } = new THREE.Vector3().randomDirection().multiplyScalar(r);
        positions.push(x, y, z);
      } else {
        // 外围圆盘粒子
        let r = 10;
        let R = 40;
        let rand = Math.pow(Math.random(), 1.5);
        let radius = Math.sqrt(R * R * rand + (1 - rand) * r * r); // 通过 rand=0-1 数值去线性插值 R^2 和 r^2 大概是按圆圈面积采样粒子分布更均匀
        let { x, y, z } = new THREE.Vector3().setFromCylindricalCoords(
          radius, // 半径
          Math.random() * 2 * Math.PI, // 角度
          (Math.random() - 0.5) * 2 // 高度y -1-1
        );
        positions.push(x, y, z);
      }
    }

    // geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
    // geometry.setAttribute('aSize', new THREE.Float32BufferAttribute(sizes, 1));
    // geometry.setAttribute('aShift', new THREE.Float32BufferAttribute(shifts, 4));

    geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
    geometry.setAttribute('aSize', new THREE.Float32BufferAttribute(sizes, 1));
    geometry.setAttribute('aShift', new THREE.Float32BufferAttribute(shifts, 4));
    const material = new THREE.ShaderMaterial({
      uniforms: {
        uColor: { value: new THREE.Color(this.controls.color) },
        uTime: { value: 0 },
      },
      transparent: true,
      blending: THREE.AdditiveBlending,
      depthTest: false,
      vertexShader: `
        attribute float aSize;
        attribute vec4 aShift;

        uniform float uTime;

        varying vec3 vColor;

        const float PI = 3.141592653589793238;

        void main() {
            // float d = abs(position.y) / 10.0;
            float d = length(abs(position) / vec3(40., 10., 40.)); // 中间黄色、外面紫色
            d = clamp(d, 0., 1.);
            
            // rgb(227, 155, 0)
            // rgb(100, 50, 255)
            vec3 color1 = vec3(227., 155., 0.);
            vec3 color2 = vec3(100., 50., 255.);

            vColor = mix(color1, color2, d) / 255.;

            vec3 transformed = position;

            float theta = mod(aShift.x + aShift.z * uTime, PI * 2.);
            float phi = mod(aShift.y + aShift.z * uTime, PI * 2.);
            transformed += vec3(sin(phi) * cos(theta), cos(phi), sin(phi) * sin(theta)) * aShift.w;
            
            vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.0);
            gl_PointSize = aSize * 50.0 / -mvPosition.z;
            gl_Position = projectionMatrix * mvPosition;
        }
      `,
      fragmentShader: `
        varying vec3 vColor;
        void main() {
          float d = length(gl_PointCoord.xy - 0.5);
          if (d > 0.5) discard;
          // gl_FragColor = vec4(vColor, step(0.5, 1.0 - d));
          gl_FragColor = vec4(vColor, smoothstep(0.5, 0.1, d));
        }
      `,
    });
    const points = new THREE.Points(geometry, material);
    points.rotation.order = 'ZYX';
    points.rotation.z = 0.2;
    this.scene.add(points);
    this.camera.position.set(0, 3, 24);
    this.camera.lookAt(this.scene.position);
    // console.log(vShader)
    // const g =  new THREE.IcosahedronGeometry(10, 6);
    // const m = new THREE.ShaderMaterial({
    //   uniforms: {
    //     uColor: { value: new THREE.Color(this.controls.color) },
    //     uTime: { value: 0 } ,
    //   },
    //   vertexShader: vShader ||`
    //     varying vec2 vUv;
    //     void main() {
    //       vUv = uv;
    //       gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    //     }
    //   `,
    //   fragmentShader: fShader,
    //   wireframe: wireframe,

    // });
    // const plane = new THREE.Mesh(g, m);
    // plane.position.x=x
    // plane.position.y=y
    // this.scene.add(plane);
  }
  renderContent() {
    this.time += 0.05;
    let time = this.clock.getElapsedTime();
    for (let i = 0; i < this.scene.children.length; i++) {
      this.scene.children[i].material.uniforms.uTime.value = time;
      this.scene.children[i].rotation.y = this.time / 100;
    }
  }
}

let ins;
const dom = ref();

onMounted(() => {
  ins = new Demo().init(dom.value);
});
onUnmounted(() => {
  ins.dispose();
});

const text = `
本示例主要学习了通过 uniforms 向着色器传递数据。示例主要代码如下：
\`\`\`
const m = new THREE.ShaderMaterial({
  uniforms: {
    uColor: { value: new THREE.Color(0xff0000) }
  },
  vertexShader: \`
    void main() {
      
      // position 是顶点的local坐标(Model坐标)
      // Model坐标 * ModelMatrix = World坐标；World坐标 * ViewMatrix = Camera坐标；Camera坐标 * ProjectionMatrix = 投影坐标

      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }\`,
  fragmentShader: \`
    uniform vec3 uColor;  // 接收 uniforms 传过来的数据
    
    void main() {
      gl_FragColor = vec4(uColor, 1.0);
    }\`
});
\`\`\`
`;
</script>

<template>
  <div class="shader-box" ref="dom"></div>
  <v-md-preview :text="text"></v-md-preview>
</template>

<style scoped></style>
